package logic

import (
	"context"
	"fmt"
	"strconv"
	"time"

	"github.com/axengine/utils/hash"
	"github.com/go-redis/redis/v8"

	"github.com/zeromicro/go-zero/core/logx"
	"gorm.io/gorm"
)

type Model struct {
	ID        int32     `gorm:"primarykey" json:"id"`
	CreatedAt time.Time `json:"createdAt"`
	UpdatedAt time.Time `json:"updatedAt"`
	//DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
}

type Rule struct {
	DeviceId string
	AppId    string
	DstIP    string
	DstPort  string
	Status   int
	UserId   string
}

func (r *Rule) Key() string {
	return hash.MD5Lower([]byte(fmt.Sprintf("%s%s%s%s%s",
		r.DeviceId, r.AppId, r.DstIP, r.DstPort, r.UserId)))
}

type App struct {
	Model
	AppName  string `gorm:"comment:应用名称;type:VARCHAR(255)"`
	AppId    string `gorm:"comment:应用ID;type:VARCHAR(64);uniqueIndex:IDX_APP_DEV"`
	DeviceId string `gorm:"comment:设备ID;type:VARCHAR(64);uniqueIndex:IDX_APP_DEV"`
	Memo     string `gorm:"comment:备注;type:VARCHAR(255)"`
	UserId   string `gorm:"comment:用户ID;type:VARCHAR(64);uniqueIndex:IDX_APP_DEV"`
}

type Warning struct {
	ID        int32     `gorm:"primarykey" json:"id"`
	CreatedAt time.Time `json:"createdAt"`
	AppName   string    `gorm:"comment:应用名称;type:VARCHAR(255);index"`
	AppId     string    `gorm:"comment:应用ID;type:VARCHAR(64);index:IDX_APP_DEV"`
	DeviceId  string    `gorm:"comment:设备ID;type:VARCHAR(64);index:IDX_APP_DEV"`
	Memo      string    `gorm:"comment:设备备注;type:VARCHAR(255)"`
	DstIP     string    `gorm:"comment:目的IP;type:VARCHAR(32);index"`
	DstPort   int       `gorm:"comment:目的端口;type:INT(11);index"`
	Desc      string    `gorm:"comment:备注;type:VARCHAR(255)"`
	Level     int       `gorm:"comment:等级;type:INT(11)"`
}

type Whitelist struct {
	Model
	AppName  string `gorm:"comment:应用名称;type:VARCHAR(255)"`
	AppId    string `gorm:"comment:应用ID;type:VARCHAR(64);uniqueIndex:IDX_APP_DEV"`
	DeviceId string `gorm:"comment:设备ID;type:VARCHAR(64);uniqueIndex:IDX_APP_DEV"`
	Memo     string `gorm:"comment:备注;type:VARCHAR(255)"`
}

func (w *Whitelist) Key() string {
	return "whitelist" + "_" + w.AppId + "_" + w.DeviceId
}

type TrustEstimateRo2 struct {
	DeviceId   string `json:"deviceId"`
	AppId      string `json:"appId"`
	Level      string `json:"level"`
	Agreement  string `json:"agreement"`
	TargetIp   string `json:"targetIp"`
	TargetPort string `json:"targetPort"`
	UserId     string `json:"userId"`
}

type TrustEstimateRsp2 struct {
	Status bool   `json:"status"`
	Msg    string `json:"msg"`
	Code   string `json:"code"`
}

func TrustEstimate2(req *TrustEstimateRo2, r *redis.Client, db *gorm.DB) (resp *TrustEstimateRsp2, err error) {
	resp = &TrustEstimateRsp2{
		Status: false,
		Msg:    "",
		Code:   "002",
	}

	// 检测规则
	rule := Rule{
		DeviceId: req.DeviceId,
		AppId:    req.AppId,
		DstIP:    req.TargetIp,
		DstPort:  req.TargetPort,
		UserId:   req.UserId,
	}
	//	fmt.Println("---trustRuleKey:", rule.Key())
	//isMember, err := l.svcCtx.RedisClient.SismemberCtx(l.ctx, model.RuleSetKey, rule.Key())
	isMember, err := r.SIsMember(context.TODO(), "RuleSetKey", rule.Key()).Result()

	if err != nil {
		resp.Msg = err.Error()
		return resp, nil
	}
	if isMember {
		resp.Status = true
		resp.Code = "001"
		return
	}

	if !isMember {
		resp.Msg = "blocked"
		go func() {
			var app App
			if err := db.Where("AppId=?", req.AppId).
				Where("DeviceId=?", req.DeviceId).First(&app); err != nil {
				return
			}
			port, _ := strconv.ParseInt(req.TargetPort, 10, 32)
			if err := db.Create(&Warning{
				AppName:  app.AppName,
				AppId:    req.AppId,
				DeviceId: req.DeviceId,
				Memo:     app.Memo,
				DstIP:    req.TargetIp,
				DstPort:  int(port),
				Desc:     resp.Msg,
				Level:    1, //todo
			}).Error; err != nil {
				logx.Error(err)
			}
		}()
		return resp, nil
	}

	// 检测是否是白名单
	var whitelist = Whitelist{
		AppId:    req.AppId,
		DeviceId: req.DeviceId,
	}
	//exist, err := r.ExistsCtx(context.TODO(), whitelist.Key())
	exist, err := r.Exists(context.TODO(), whitelist.Key()).Result()
	if err != nil {
		resp.Msg = err.Error()
		return resp, err
	}
	if exist > 0 {
		resp.Status = true
		resp.Code = "001"
		return resp, nil
	}

	return
}
